# 段页式管理，先分段、后分页
# 线性地址空间4GB，页大小4KB，物理地址空间16MB

# 字节
class Byte:
    def __init__(self, val=0):
        self.val = val & 0xff

# 16位拼接
def Byte16_montage(byte1, byte0):
    return (byte1 << 8) | byte0

# 32位拼接
def Byte32_montage(byte3, byte2, byte1, byte0):
    return (Byte16_montage(byte3, byte2) << 16) | Byte16_montage(byte1, byte0)

# 16位转化成字节
def Bit16_to_byte(bits):
    byte1 = (bits >> 8) & 0xff
    byte0 = bits & 0xff
    return byte1, byte0

# 32位转化成字节
def Bit32_to_byte(bits):
    byte3, byte2 = Bit16_to_byte((bits >> 16) & 0xffff)
    byte1, byte0 = Bit16_to_byte(bits & 0xffff)
    return byte3, byte2, byte1, byte0


# 存储器
class Memory:
    def __init__(self,size):
        self.size = size        # 16MB = 16,777,216
        self.data = [Byte() for i in range(size)]

    def Data32_port_out(self, addr):
        byte0, byte1, byte2, byte3 = self.data[addr].val, self.data[addr + 1].val, self.data[addr + 2].val, self.data[addr + 3].val
        return Byte32_montage(byte3, byte2, byte1, byte0)

    def Data32_port_in(self, bits, addr):
        byte3, byte2, byte1, byte0 = Bit32_to_byte(bits)
        self.data[addr] = Byte(byte0)
        self.data[addr + 1] = Byte(byte1)
        self.data[addr + 2] = Byte(byte2)
        self.data[addr + 3] = Byte(byte3)

    def Data16_port_in(self, bits, addr):
        byte1, byte0 = Bit16_to_byte(bits)
        self.data[addr] = Byte(byte0)
        self.data[addr + 1] = Byte(byte1)

    def Data16_port_out(self, addr):
        byte0, byte1 = self.data[addr].val, self.data[addr + 1].val
        return Byte16_montage(byte1, byte0)

    def Data8_port_in(self, bits, addr):
        self.data[addr] = Byte(bits)

    def Data8_port_out(self, addr):
        return self.data[addr].val


# 页表项
class Page_table_entry:
    def __init__(self, valid=False, page_frame=-1):
        self.valid = valid              # 有效位
        self.page_frame = page_frame    # 页帧

# 页表和FIFO队列
# 内存管理单元
class MMU:
    # 1<<20 = 4GB/4KB    4096 = 16MB/4KB      256 = 1MB/4KB    4 = 16KB/4KB    1 = 4KB/4KB
    def __init__(self, linear_address_size=(1<<20), physical_address_size=4):
        self.qe = Queue(physical_address_size)  # 页帧号最大是2^12-1
        self.page_table = [Page_table_entry() for i in range(linear_address_size)]

    def Linear2Physical(self, virtual_address, mem, segment = 1):
        # virtual_address = Handle(virtual_address,segment)
        page_num = virtual_address >> 12
        if self.page_table[page_num].valid:
            page_frame = self.page_table[page_num].page_frame
            return (page_frame << 12) | (virtual_address & 0xfff)   # 返回物理地址
        else: # 缺页
            Do_no_page(self.qe, page_num, self.page_table, mem, segment)
            page_frame = self.page_table[page_num].page_frame
            return (page_frame << 12) | (virtual_address & 0xfff)     # 返回物理地址
    

class Queue:
    def __init__(self,size):
        self.size = size    # 队列的大小4096
        self.data = []      # valid=1 的 (页号,页帧)对
        self.count = 0       # 队列中的元素个数
    
    # 放入
    def Put(self,page_num,page_frame):
        if self.count + 1 <= self.size:
            self.count += 1
            self.data.append((page_num,page_frame))
    
    # 取出
    def Get(self):
        if self.count > 0:
            self.count -= 1
            return self.data.pop(0)

    # 判断
    def __contains__(self, val):
        for i in range(self.count):
            if self.data[i][1] == val:
                return True
        return False


# 缺页处理函数  页面替换策略FIFO
def Do_no_page(qe:Queue, page_num:int, page_table:list, mem:Memory, segment):
    if qe.count >= qe.size:
        val = qe.Get()
        # page_frame = page_table[val[0]].page_frame

        # # 写回 一个页
        # physical_address = page_frame << 12
        # filepath = 'DISK/'+str(val[0])
        # with open(filepath,'wb') as f:
        #     # 从内存中读取 写回 外存
        #     for i in range(0x1000):
        #         value = mem.data[physical_address+i].val.to_bytes(1,'little')
        #         f.write(value)
        page_table[val[0]].page_frame = -1
        page_table[val[0]].valid = False

    # 寻找合适的页帧
    page_frame = -1
    for i in range(qe.size):
        if i not in qe:
            page_frame = i
    physical_address = page_frame << 12
    if segment==0:
        filepath = 'DISK/'+str(page_num)
        with open(filepath,'rb') as f:
            # 读外存， 存到 内存中
            for i in range(0x1000//4):
                value = f.read(32)             # 读4字节，是否有回车需要考虑-----------------------------------------------
                if(value): mem.Data32_port_in(int(value,2),physical_address)         #-------------------------可能要改
                physical_address += 4
                value = f.read(2)
                # mem.data[i+physical_address] = Byte(int.from_bytes(value, "little"))

    page_table[page_num].page_frame = page_frame
    page_table[page_num].valid = True
    qe.Put(page_num, page_frame)


sttr = ['bubble_sort_test/bubble_sort_bin','Fibonacci_test/fib_10th_1_bin','Fibonacci_test/fib_30th_bin']
num = [41,173,183]
# 标准初始化（外存）
def Real_init(index):
    global str,num
    infile = open(sttr[index], "rb")   #   bubble_sort_bin  fib_10th_1_bin   fib_10th_2_bin  fib_30th_bin

    # 虚拟地址, 自己给出--------------------------------------------------------
    virtual_address = 0x00400000

    save_size = num[index]*4                 # 要存的字节数,需要修改-41-173-183-------------------------------------

    while(save_size>0):
        page_num = virtual_address >> 12
        filepath = 'DISK/'+str(page_num)
        with open(filepath,'wb') as f:
            for i in range(min(0x1000//4,save_size//4)):
                value = infile.read(32)          # 读取32位-------------------------------------是否有回车需要考虑
                f.write(value)
                value = infile.read(2)           # 回车------------------------------------------
                f.write(value)
        save_size -= min(0x1000,save_size)
        virtual_address += min(0x1000,save_size)
    infile.close() 


if __name__ == "__main__" :
    # 运行实验号
    SHIYAN = 1              # 0 1 2
    Real_init(SHIYAN)
    print('预处理完成！')   